package com.gcloud.mesh.distributedSchedule.evaluateFactor;

import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.gcloud.mesh.distributedSchedule.ICluster;
import com.gcloud.mesh.distributedSchedule.IEvaluateFactor;
import com.gcloud.mesh.distributedSchedule.application.Application;

/**
 * 性能评价函数, sum(调度到该集群的app数量*该集群的性能分数)
 */
public class PerformanceFactor extends EvaluateFactor implements IEvaluateFactor {

    public static String nameId = "performance_factor";

    // 性能影响度系数,以下是经验值，可以酌情调整
    private double cpu = 1.0;
    private double mem = 0.6;
    private double storage = 0.8;
    private double bandwidth = 0.6;

    // TODO:
    private int iops = 0;

    /**
     * @param scheduleAppList  要调度的应用
     * @param targetClusters   app要调度到的目标集群
     * @param clusterIds       应用要调度到的集群id列表，顺序和 scheduleAppList 保持一致
     * @return
     */
    @Override
    public evalResult evaluate(Application[] scheduleAppList, ConcurrentHashMap<String, ICluster> targetClusters, String[] clusterIds) {
        evalResult er = new evalResult(nameId);
        HashMap<String, Set<Integer>> cltSort = this.sort(clusterIds);

        er.qualified = true;
        for (String cltId : cltSort.keySet()) {
            int l2cache = targetClusters.get(cltId).getL2CacheSizeKB();
            int cpuCore = targetClusters.get(cltId).getNodeCpuCoreNum();
            double memSizeG = targetClusters.get(cltId).getNodeMemSizeG();
            double frequency = targetClusters.get(cltId).getNodeCpuHighestFrequency();
            double performanceScore = this.performanceModel(frequency, l2cache, cpuCore, memSizeG);

            /**
             * 迁移到该集群的应用数量 * 该集群的性能评估分数 = 该集群的性能分数
             * Set集合里面的迁移应用集合，有些是真的迁移，有些可能没有迁移，还在原来的集群上。
             * 这部分应用仍然统计入性能分数里，因为如果当前集群确实是高性能集群，那么不迁移应用正是一种追求高性能的行为。
             */
            er.score += cltSort.get(cltId).size() * performanceScore;
        }

        return er;
    }

    private double performanceModel(double cpuFrequencyG, int L2CacheKB, int htCoresNum, double memSizeG) {
        return  sigmoidF(cpuFrequencyG * this.cpu) *
                sigmoidF(L2CacheKB / 256) *
                sigmoidF(htCoresNum / 2) *
                sigmoidF(this.mem * memSizeG / 8);
    }

    private double sigmoidF(double x) {
        double y = 1.0;
        return 1f / (1f + y * Math.pow(Math.E, -1 * x));
    }
}
